DartVM PortMap
介绍
PortMap 用于管理所有 Isolate 的端口,它在虚拟机中是一个单例。
PortMap 在虚拟机中有 2 个,Dart 层有一个,C/C++ 层有一个。
PortMap(Dart)
位于 _RawReceivePortImpl 类中,是一个静态成员,签名:
static final _portMap = <int, Map<String, dynamic>>{};
PortMap(C++)
位于 runtime/vm/port.h,该类继承自 AllStatic,是一个纯静态类。
PortSet 数据结构
PortMap 中维护了一个数据结构 PortSet(runtime/vm/port_set.h)具体结构还没有细看,暂且理解为一个哈西表,声明为:
static PortSet<Entry>* ports_;
Entry 是一个 struct,结构如下:
![]() |
struct Entry : public PortSet<Entry>::Entry {
Entry() : handler(nullptr), state(kNewPort) {}
MessageHandler* handler;
PortState state;
};
|
整体结构图:
其中:PortState 表示端口的生命周期,共有 4 个:kNewPort、kLivePort、kControlPort、kInactivePort。
CreatePort 创建端口
创建一个端口,拥有虚拟机全局唯一 id:
static Dart_Port CreatePort(MessageHandler* handler);
实现:
Dart_Port PortMap::CreatePort(MessageHandler* handler) {
//……
// 创建一个全局唯一的端口号
const Dart_Port port = AllocatePort();
// The MessageHandler::ports_ is only accessed by [PortMap], it is guarded
// by the [PortMap::mutex_] we already hold.
// 将端口号也告诉 handler
MessageHandler::PortSetEntry isolate_entry;
isolate_entry.port = port;
handler->ports_.Insert(isolate_entry);
// 这是 portSet 的 Entry
Entry entry;
entry.port = port;
entry.handler = handler;
entry.state = kNewPort;
ports_->Insert(entry);
return entry.port;
}
其中:
- 全局唯一端口号是在这里分配的
- 端口号会记录在 MessageHandler 中
- 将新端口-handler保存到 PortSet 中
最广泛的使用处是 RawReceivePortImpl_factory 方法:
DEFINE_NATIVE_ENTRY(RawReceivePortImpl_factory, 0, 2) {
ASSERT(
TypeArguments::CheckedHandle(zone, arguments->NativeArgAt(0)).IsNull());
GET_NON_NULL_NATIVE_ARGUMENT(String, debug_name, arguments->NativeArgAt(1));
Dart_Port port_id = PortMap::CreatePort(isolate->message_handler());
return ReceivePort::New(port_id, debug_name, false /* not control port */);
}
现在知道了:
- 传入的 isolate->message_handler,新创建的端口会存入 message_handler 的 ports_
- isolate->message_handler 会同事关联多个 port(ReceivePort)
- port 的 id 同时也会存入 ReceivePort 的 id 中
PostMessage 发消息
向 Port 发消息的底层方法,就是 PostMessage。
bool PortMap::PostMessage(std::unique_ptr<Message> message,
bool before_events) {
MutexLocker ml(mutex_);
auto it = ports_->TryLookup(message->dest_port());
if (it == ports_->end()) {
// Ownership of external data remains with the poster.
message->DropFinalizers();
return false;
}
MessageHandler* handler = (*it).handler;
ASSERT(handler != nullptr);
handler->PostMessage(std::move(message), before_events);
return true;
}
其中:
- 消息中是包含端口号的,首先从 PortMap 中根据端口号拿出 Entry
- 从 Entry 中就能拿到 handler
- 然后调用 handler 的 PostMessage
- 可以看处 PortMap 只是一个转发过程,具体向消息队列加消息的操作,是由 MessageHandler 自己实现的